/**********************************************************************************\
** 文件名称: heap.c
** 创建人员: YCG
** 创建日期: 2019-01-14
** 文档描述:
** 版本代号: V0.2
** 版本说明: 初始版本
1.优化注释.
2.目前没有中断保护代码，不建议在中断中使用
\********************************************************************************/
#include	"heap.h"

#ifndef  USE_FREERTOS_HEAP
__align(8) static uint8_t HeapBuf[HEAP_SIZE];

//总体的内存结构为   内存块头结构体|空闲内存块|内存块尾结构体
static Heap_t * const HeapStart =     (Heap_t*)  HeapBuf;                                            //内存块链头结构体
static Heap_t * const HeapFirstFree = (Heap_t*) ((uint8_t*)HeapBuf + HEAP_STRUCT_SIZE);              //第一个空闲内存块
static Heap_t * const HeapEnd =       (Heap_t*) ((uint8_t*)HeapBuf + HEAP_SIZE - HEAP_STRUCT_SIZE);  //链尾指针,指向缓存的最后位置

static uint32_t HeapFreeBytes = HEAP_INIT_FLAG;    //内存堆剩余大小
static uint32_t HeapMinFreeBytes ;                 //最少内存剩余量

static void HeapInit(void);
static void HeapInsertFreeList(Heap_t *yInsert);

/*******************************************************************************
** 函数名称: MyMalloc
** 功能描述: 分配内存空间
** 参数说明: ySize: [输入] 空间大小
** 返回说明: 分配内存的地址
** 创建人员: YCG
** 创建日期: 2019-01-15
********************************************************************************/
void* MyMalloc(uint32_t ySize) {

    Heap_t *yIterator;   //用于遍历获取空闲块的位置
    Heap_t *yHead;	      //用于操作当前的空闲内存块
    Heap_t *yNewHead;	  //用于分裂当前的空闲内存块

    void* yReturn = NULL;   //用来保存返回的地址，分配内存失败返回为空
    HEEP_ENTER_PROTECT();
    if(HeapFreeBytes == HEAP_INIT_FLAG) {        //通过初始化标志来判断是否初始化过
        HeapInit();
    }

    ySize += HEAP_STRUCT_SIZE;                  //分配内存空间的大小需要加上头部结构体的大小

    uint32_t AllgnmentSize =	(ySize & HEAP_BYTE_ALIGNMENT_MASK);   //计算未对齐的值

    if(AllgnmentSize != 0x00) {                                     //对齐
        ySize += (HEAP_BYTE_ALIGNMENT - AllgnmentSize);             //加上未对齐的差值，按要求对齐，避免硬件错误
    }

    for( yIterator = HeapStart; yIterator->Next->Size < ySize; yIterator = yIterator->Next )
    {
        //查找符合需求的空闲块的位置,HeapEnd->Size为最大值，到达末尾时会结束循环
    }
    yHead = yIterator->Next;     //获取空闲内存块的位置

    if(yHead != HeapEnd) {        //判断有没有内存可以分配

        yReturn = (void*)((uint8_t*)yHead + HEAP_STRUCT_SIZE);   //获取申请到内存的指针
        yIterator->Next = yHead->Next;                           //将内存从空闲列表中删除
        yHead->Next = HEAP_USE_ADD_FLAG;                         //已经使用的内存块的下一个地址标为空

        if(yHead->Size - ySize > HEAP_MINI_BLOCK_SIZE) {         //如果剩余内存大于最小的缓存，就分成两块
            yNewHead = (void*)((uint8_t*)yHead + ySize);        //获取新空闲内存的指针
            yNewHead->Size = yHead->Size - ySize;               //计算新空闲内存的大小
            yHead->Size = ySize;                                 //计算原内存的大小
            HeapInsertFreeList(yNewHead);                         //插入到空闲内存列表中
        }

        HeapFreeBytes -= yHead->Size;           //计算剩余内存空间
        if(HeapFreeBytes < HeapMinFreeBytes) {
            HeapMinFreeBytes = HeapFreeBytes ;  //更新内存的最小剩余量
        }
    }
    HEEP_EXIT_PROTECT();
    return yReturn;
}

/*******************************************************************************
** 函数名称: MyFree
** 功能描述: 释放内存
** 参数说明: pv: [输入] 内存地址
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-15
********************************************************************************/
void MyFree(void *pv) {
    Heap_t *yHead;	                   //用于操作当前的空闲内存块

    if( pv != NULL )
    {
        yHead = (Heap_t*)((uint8_t*)pv - HEAP_STRUCT_SIZE);//计算内存块头部的位置

        if( yHead->Next  == HEAP_USE_ADD_FLAG )         //判断当前内存是否被使用
        {
            HEEP_ENTER_PROTECT();
            HeapFreeBytes += yHead->Size; //计算剩余内存空间
            HeapInsertFreeList(yHead);    //插入到空闲内存列表中
            HEEP_EXIT_PROTECT();
        }
    }

}

/*******************************************************************************
** 函数名称: HeapInit
** 功能描述: 初始化 内存块头结构体|空闲内存块|内存块尾结构体
** 参数说明: None
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-15
********************************************************************************/
static void HeapInit(void) {

    HeapStart->Next = HeapFirstFree;    //初始化之后的下一个为末尾的
    HeapStart->Size = 0;                //只是用来定位的

    HeapFirstFree->Next = HeapEnd;      //初始化之后的下一个为末尾的
    HeapFirstFree->Size = HEAP_SIZE - (2 * HEAP_STRUCT_SIZE);     //剩余大小为总大小减去首尾头部的大小

    HeapEnd->Next = HeapEnd;            //末尾指向本身
    HeapEnd->Size = HEAP_END_SIZE;      //最大值，分配内存时，到这里总会满足条件

    HeapMinFreeBytes = HeapFirstFree->Size;	  //最小内存块大小
    HeapFreeBytes    = HeapFirstFree->Size;	  //剩余空间等于空闲块的大小
}

/*******************************************************************************
** 函数名称: HeapInsertFreeList
** 功能描述: 将释放的内存插入到空闲内存列表
** 参数说明: yInsert: [输入] 释放的内存地址
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-15
********************************************************************************/
static void HeapInsertFreeList(Heap_t *yInsert) {

    Heap_t *yIterator;          //用于遍历插入的位置
    Heap_t *yIteratorNext;      //插入的位置的下一个位置

    for( yIterator = HeapStart; yIterator->Next < yInsert; yIterator = yIterator->Next )
    {
        //获取第一个空闲内存块的位置。遍历查找插入的位置，插入在yIterator之后
        //具体位置 yIterator |<| yInsert |<|yIterator->Next
    }

    /*前向合并*/ // 具体位置 yIterator + yIterator->Size == yInsert
    if(((uint8_t*)yIterator + (uint32_t)yIterator->Size) == ((uint8_t*)yInsert)) {	//yIterator之后紧接着yInsert时进行合并
        yIterator->Size += yInsert->Size;  //合并大小
        yInsert = yIterator;               //更新合并的内存块为新插入的空闲内存块
    }

    /*后向合并*/
    yIteratorNext = yIterator->Next; // 具体位置 yInsert + yInsert->Size == yIterator->Next
    if(((uint8_t*)yInsert + (uint32_t)yInsert->Size) == ((uint8_t*)(yIteratorNext)))  //yInsert之后紧接着yIterator的下一个时进行合并 //如果是连续的
    {
        if(yIteratorNext != HeapEnd)                    //判断是不是末尾
        {
            yInsert->Size += yIteratorNext->Size;         //合并大小
            yInsert->Next = yIteratorNext->Next;          //更新插入
        } else {                                          //不是末尾
            yInsert->Next = HeapEnd;                      //如果是末尾,则不合并，因为末尾是无效的
        }
    } else {                                              //没有后向合并
        yInsert->Next = yIteratorNext;                    //更新新插入的空闲内存块的下一个连接空闲快
    }

    if(yIterator != yInsert) {              //没有前向合并
        yIterator->Next = yInsert;          //更新新插入的空闲内存块的上一个连接空闲快
    }
}

/*******************************************************************************
** 函数名称: GetHeapFreeBytes
** 功能描述: 获取剩余空间的大小
** 参数说明: None
** 返回说明: 剩余空间的大小
** 创建人员: YCG
** 创建日期: 2019-01-15
********************************************************************************/
uint32_t GetHeapFreeBytes(void) {
    return HeapFreeBytes;
}

/*******************************************************************************
** 函数名称: GetHeapMinFreeBytes
** 功能描述: 获取最小剩余内存
** 参数说明: None
** 返回说明: 最小剩余内存
** 创建人员: YCG
** 创建日期: 2019-01-15
********************************************************************************/
uint32_t GetHeapMinFreeBytes(void) {
    return HeapMinFreeBytes;
}

#endif
/*******************************************************************************
** 函数名称: MyFreeUnuse
** 功能描述: 释放掉已经分配了，但未使用的内存
** 参数说明: pv: [输入] 分配内存的地址
**			 ySize: [输入] 已经使用的大小
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-17
********************************************************************************/
void MyFreeUnuse(void *pv, uint32_t ySize) {
#ifndef  USE_FREERTOS_HEAP
    Heap_t *yHead;	                   //用于操作当前使用的内存块
    Heap_t *yNewHead;	               //用于分裂当前的空闲内存块

    if( pv != NULL )
    {
        yHead = (Heap_t*)((uint8_t*)pv - HEAP_STRUCT_SIZE);    //计算内存块头部的位置

        if( yHead->Next  == HEAP_USE_ADD_FLAG )                //只有在内存被使用的情况下才释放掉未被使用的空间
        {
            ySize += HEAP_STRUCT_SIZE;                                       //加上头部的大小

            uint32_t AllgnmentSize =	(ySize & HEAP_BYTE_ALIGNMENT_MASK);    //未对齐的值
            if(AllgnmentSize != 0x00) {                                      //对齐
                ySize += (HEAP_BYTE_ALIGNMENT - AllgnmentSize);              //加上未对齐的差值
            }
            HEEP_ENTER_PROTECT();
            if(yHead->Size - ySize > HEAP_MINI_BLOCK_SIZE) {          //如果剩余内存大于最小的缓存，就分成两块
                yNewHead = (void*)((uint8_t*)yHead + ySize);          //获取新空闲内存的指针
                yNewHead->Size = yHead->Size - ySize;                 //计算新空闲内存的大小
                yHead->Size = ySize;                                  //计算原内存的大小
                HeapInsertFreeList(yNewHead);                         //插入到空闲内存列表中
            }
            HEEP_EXIT_PROTECT();
        }
    }
#else

#endif

}
/********************************End of File************************************/

